package modele;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import modele.exceptions.EtudiantInexistantException;
import modele.exceptions.FormationInexistanteException;
import modele.exceptions.InformationIndisponibleException;
import modele.exceptions.IntervenantInexistantException;
import modele.exceptions.MaitreDeStageInexistantException;
import modele.exceptions.MatiereInexistanteException;
import modele.exceptions.PasDeMaitreDeStageException;
import modele.exceptions.PasDeTuteurException;

/**
 * Class Etudiant permet de représenter un étudiant, et de savoir ses informations,
 * son tuteur, son maître de stage, sa formation et son année.
 */
public class Etudiant extends Personne
{
    private Intervenant tuteur; //valeur non obligatoire
    private MaitreDeStage maitreDeStage; //valeur non obligatoire
    private Formation formation;
    private Annee annee;
    
    /**
     * Constructeur permettant de remplir toutes les informations de l'étudiant.
     * L'identifiant est unique pour un étudiant, mais pas par rapport à Personne.
     */
    public Etudiant(int idetu, Intervenant tuteur, MaitreDeStage maitre, Formation form, Annee annee, String nom, String prenom, String adresse, String email, String tel)
    {
        super(idetu, nom, prenom, adresse, email, tel, "", "");

        this.tuteur = tuteur;
        this.maitreDeStage = maitre;
        this.formation = form;
        this.annee = annee;
    }

    /**
     * Constructeur partiel de Etudiant, ne doit être utilisé que lorsqu'on n'a
     * pas besoin des autres informations
     */
    public Etudiant(int id, String nom, String prenom, Formation form, Annee annee) {
        this(id, null, null, form, annee, nom, prenom, null, null, null);
    }

    /**
     * Constructeur partiel de Etudiant, ne doit être utilisé que lorsqu'on n'a
     * pas besoin des autres informations
     * @param id : identifiant de l'étudiant
     * @param nom : nom de l'étudiant
     * @param prenom : prénom de l'étudiant
     */
    public Etudiant(int id, String nom, String prenom) {
        this(id, null, null, null, null, nom, prenom, null, null, null);
    }

    /**
     * Constructeur partiel de Etudiant, ne doit être utilisé que lorsqu'on n'a
     * pas besoin des autres informations
     * @param id : identifiant de l'étudiant
     */
    public Etudiant(int id) {
        this(id, null, null, null, null, null, null, null, null, null);
    }

    /**
     * Indique si l'étudiant a un tuteur
     * @return true si l'étudiant a un tuteur, false sinon
     */
    public boolean aUnTuteur()
    {
        return tuteur != null;
    }
    
    /**
     * Indique si l'étudiant a un maitre de stage
     * @return true si l'étudiant a un maitre de stage, false sinon
     */
    public boolean aUnMaitreDeStage()
    {
        return maitreDeStage != null;
    }
    
    /**
     * Renvoie le tuteur de l'étudiant s'il en a un, une exception sinon
     * @return le tuteur de l'étudiant
     * @throws PasDeTuteurException si l'étudiant n'a pas de tuteur
     */
    public Intervenant getTuteur() throws PasDeTuteurException {
        if (tuteur == null) 
            throw new PasDeTuteurException();
        
        return tuteur;
    }

    /**
     * Renvoie le maître de stage de l'étudiant s'il en a un, une exception sinon
     * @return le maître de stage de l'étudiant
     * @throws PasDeMaitreDeStageException si l'étudiant n'a pas de maitre de stage
     */
    public MaitreDeStage getMaitreDeStage() throws PasDeMaitreDeStageException {
        if (maitreDeStage == null) 
            throw new PasDeMaitreDeStageException();
        
        return maitreDeStage;
    }

    /**
     * Renvoie la formation de l'étudiant si elle est renseignée, une exception sinon
     * @return la formation de l'étudiant
     * @throws InformationIndisponibleException
     */
    public Formation getFormation() throws InformationIndisponibleException {
        if(formation == null)
            throw new InformationIndisponibleException(Formation.class, this.getClass());

        return formation;
    }

     /**
     * Renvoie l'année de l'étudiant si elle est renseignée, une exception sinon
     * @return l'année de l'étudiant
     * @throws InformationIndisponibleException
     */
    public Annee getAnnee() throws InformationIndisponibleException {
        if(annee == null)
            throw new InformationIndisponibleException(Annee.class, this.getClass());

        return annee;
    }

    /**
     * Renvoie le retour du tuteur sur l'étudiant s'il en a un, une exception sinon.
     * Si le tuteur n'a pas encore formulé de retour sur l'étudiant, il sera créé à vide.
     * @return le retour demandé avec seulement son texte
     * @throws PasDeTuteurException si l'étudiant n'a pas de tuteur
     */
    public Retour getRetourTuteur() throws PasDeTuteurException, SQLException
    {
        if (tuteur == null)
            throw new PasDeTuteurException();

        Retour r = null;

        r = RetourSurEtudiant.getRetour(Intervenant.class, tuteur.getId(), getId());

        return r;
    }

    /**
     * Renvoie le retour du maitre de stage sur l'étudiant s'il en a un, une exception sinon.
     * Si le maitre de stage n'a pas encore formulé de retour sur l'étudiant, il sera créé à vide.
     * @return le retour demandé avec seulement son texte
     * @throws PasDeMaitreDeStageException si l'étudiant n'a pas de maitre de stage
     */
    public Retour getRetourMaitreDeStage() throws PasDeMaitreDeStageException, SQLException
    {
        if (maitreDeStage == null)
            throw new PasDeMaitreDeStageException();

        Retour r = null;

        r = RetourSurEtudiant.getRetour(MaitreDeStage.class, maitreDeStage.getId(), getId());

        return r;
    }

    /**
     * Renvoie le retour de l'administration sur l'étudiant.
     * Si l'administration n'a pas encore formulé de retour sur l'étudiant, le retour sera créé à vide.
     * @return le retour demandé avec seulement son texte
     */
    public Retour getRetourAdmin() throws SQLException
    {
        Retour r = null;

        //Récupérer le retour de l'administration sur l'étudiant
        r = RetourSurEtudiant.getRetour(MembreAdministration.class, MembreAdministration.id_admin, getId());

        return r;
    }

    /**
     * Renvoie le retour de l'étudiant sur une matière si elle existe, une exception sinon.
     * Si le retour de l'étudiant sur la matière n'existe pas encore, on va le créer à vide.
     * @param id_matiere : l'identifiant de la matère demandée
     * @return le retour demandé avec seulement les identifiants de la matière et de l'étudiant
     * @throws MatiereInexistanteException si la matière demandée n'existe pas
     */
    public Retour getRetour(int id_matiere) throws MatiereInexistanteException, SQLException
    {
        Retour resultat = null;

        resultat = RetourSurMatiere.getRetour(id_matiere, getId());

        return resultat;
    }

    /**
     * Affecter un maitre de stage à l'étudiant. (avec mise à jour en base de données)
     * @param ms : le maitre de stage à affecter
     */
     public void affecterMaitreDeStage(MaitreDeStage ms) throws SQLException
     {
         maitreDeStage = ms;

         String requete = "Update etudiant_session set id_maitrestage = ? where id_etudiant = ?";
         Connection c = ConnexionBD.getConnection();
         PreparedStatement ps = c.prepareStatement(requete);
         ps.setInt(1, ms.getId());
         ps.setInt(2, getId());
         ps.executeUpdate();

         c.close();
     }

    //Méthodes statiques d'accès à la base de données

    /**
     * Renvoie l'étudiant correspondant à un identifiant s'il existe, une exception sinon.
     * @param id_etudiant : l'identifiant de l'étudiant demandé
     * @return l'étudiant demandé
     * @throws L'Etudiant est entièrement construit, donc si une de ses données est manquante, cela lève une exception.
     */ 
    public static Etudiant getEtudiant(int id_etudiant) throws EtudiantInexistantException, IntervenantInexistantException, FormationInexistanteException, MaitreDeStageInexistantException, SQLException
    {
        Etudiant resultat = null;

        String requete = "Select id_tuteur, id_maitrestage, id_formation, nom, prenom, email, adresse, tel1, id_annee from etudiant where id_etudiant = ?;";
        Connection c = ConnexionBD.getConnection();
        PreparedStatement ps = c.prepareStatement(requete);
        ps.setInt(1, id_etudiant);
        ResultSet rs = ps.executeQuery();

        if (rs.next())
        {
            //L'étudiant existe

            Integer idtuteur = (Integer) rs.getObject(1);
            Integer idms = (Integer) rs.getObject(2);
            int idform = (Integer) rs.getObject(3);
            String nom = (String) rs.getObject(4);
            String prenom = (String) rs.getObject(5);
            String email = (String) rs.getObject(6);
            String adresse = (String) rs.getObject(7);
            String tel = (String) rs.getObject(8);
            String annee = (String) rs.getObject(9);

            Intervenant tuteur = null;
            MaitreDeStage maitreDeStage = null;

            if(idtuteur != null) tuteur = Intervenant.getIntervenant(idtuteur);
            if(idms != null) maitreDeStage = MaitreDeStage.getMaitreDeStage(idms);
            Formation formation = Formation.getFormation(idform);
            Annee a = new Annee(annee);

            resultat = new Etudiant(id_etudiant, tuteur, maitreDeStage, formation, a, nom, prenom, adresse, email, tel);

        }
        else
        {
            //L'étudiant n'existe pas
            throw new EtudiantInexistantException();
        }

        c.close();

        return resultat;

    }

    /**
     * Renvoie la liste des étudiants d'une année et d'une formation
     * @param id_annee : l'identifiant de l'année demandée
     * @param id_formation : l'identifiant de la formation demandée
     * @return la liste demandée avec seulement les nom et prénom des étudiants
     */
    public static List<Etudiant> getEtudiants(String id_annee, int id_formation) throws SQLException
    {
        List<Etudiant> resultat = new ArrayList<Etudiant>();

        String requete = "Select id_etudiant, nom, prenom from etudiant where id_annee = ? and id_formation = ? order by nom, prenom;";
        Connection c = ConnexionBD.getConnection();
        PreparedStatement ps = c.prepareStatement(requete);
        ps.setString(1, id_annee);
        ps.setInt(2, id_formation);
        ResultSet rs = ps.executeQuery();

        while(rs.next())
        {
            //Pour chaque étudiant récupéré

            int id_etudiant = (Integer) rs.getObject(1);
            String nom = (String) rs.getObject(2);
            String prenom = (String) rs.getObject(3);

            resultat.add(new Etudiant(id_etudiant, nom, prenom));

        }

        c.close();

        return resultat;
    }

    /**
     * Renvoie la liste des étudiants sans maitre de stage d'une année et d'une formation
     * @param id_annee : l'identifiant de l'année demandée
     * @param id_formation : l'identifiant de la formation demandée
     * @return la liste demandée avec seulement les nom et prénom des étudiants
     */
    public static List<Etudiant> getEtudiantsSansMaitre(String id_annee, int id_formation) throws SQLException
    {
        List<Etudiant> resultat = new ArrayList<Etudiant>();

        String requete = "Select id_etudiant, nom, prenom from etudiant where id_annee = ? and id_formation = ? and id_maitrestage is null order by nom, prenom;";
        Connection c = ConnexionBD.getConnection();
        PreparedStatement ps = c.prepareStatement(requete);
        ps.setString(1, id_annee);
        ps.setInt(2, id_formation);
        ResultSet rs = ps.executeQuery();

        while(rs.next())
        {
            //Pour chaque étudiant récupéré

            int id_etudiant = (Integer) rs.getObject(1);
            String nom = (String) rs.getObject(2);
            String prenom = (String) rs.getObject(3);

            resultat.add(new Etudiant(id_etudiant, nom, prenom));

        }

        c.close();

        return resultat;
    }

    /**
     * Renvoie la liste des étudiants sans maitre de stage d'une année
     * @param id_annee : l'identifiant de l'année demandée
     * @return la liste demandée avec seulement les nom et prénom des étudiants
     */
    public static List<Etudiant> getEtudiantsSansMaitre(String id_annee) throws SQLException
    {
        List<Etudiant> resultat = new ArrayList<Etudiant>();

        String requete = "Select id_etudiant, nom, prenom from etudiant where id_annee = ? and id_maitrestage is null order by nom, prenom;";
        Connection c = ConnexionBD.getConnection();
        PreparedStatement ps = c.prepareStatement(requete);
        ps.setString(1, id_annee);
        ResultSet rs = ps.executeQuery();

        while(rs.next())
        {
            //Pour chaque étudiant récupéré

            int id_etudiant = (Integer) rs.getObject(1);
            String nom = (String) rs.getObject(2);
            String prenom = (String) rs.getObject(3);

            resultat.add(new Etudiant(id_etudiant, nom, prenom));

        }

        c.close();

        return resultat;
    }

    /**
     * Renvoie la liste des étudiants sans maitre de stage
     * @return la liste demandée avec seulement les nom et prénom des étudiants
     */
    public static List<Etudiant> getEtudiantsSansMaitre() throws SQLException
    {
        List<Etudiant> resultat = new ArrayList<Etudiant>();

        String requete = "Select id_etudiant, nom, prenom from etudiant where id_maitrestage is null order by nom, prenom;";
        Connection c = ConnexionBD.getConnection();
        PreparedStatement ps = c.prepareStatement(requete);
        ResultSet rs = ps.executeQuery();

        while(rs.next())
        {
            //Pour chaque étudiant récupéré

            int id_etudiant = (Integer) rs.getObject(1);
            String nom = (String) rs.getObject(2);
            String prenom = (String) rs.getObject(3);

            resultat.add(new Etudiant(id_etudiant, nom, prenom));

        }

        c.close();

        return resultat;
    }

    /**
     * Renvoie la liste des étudiants d'une année et d'une formation d'un tuteur ou d'un maitre de stage.
     * Pré-requis : tuteurOuMaitre doit être Intervenant ou MaitreDeStage
     * @param id_annee : l'identifiant de l'année demandée
     * @param id_formation : l'identifiant de la formation demandée
     * @param tuteurOuMaitre : obtenir la liste du tuteur ou du maitre de stage
     * @param id_tuteurOuMaitre : identifiant du tuteur ou du maitre de stage voulu
     * @return la liste demandée avec seulement les noms et prénoms des étudiants
     */
    public static List<Etudiant> getEtudiants(String id_annee, int id_formation, Class tuteurOuMaitre, int id_tuteurOuMaitre) throws SQLException
    {
        assert tuteurOuMaitre.equals(Intervenant.class) || tuteurOuMaitre.equals(MaitreDeStage.class);

        List<Etudiant> resultat = new ArrayList<Etudiant>();

        String nomId;
        if(tuteurOuMaitre.equals(Intervenant.class))
            nomId = "id_tuteur";
        else
            nomId = "id_maitrestage";

        String requete = "Select id_etudiant, nom, prenom from etudiant where id_annee = ? and id_formation = ? and " + nomId + " = ? order by nom, prenom";
        Connection c = ConnexionBD.getConnection();
        PreparedStatement ps = c.prepareStatement(requete);
        ps.setString(1, id_annee);
        ps.setInt(2, id_formation);
        ps.setInt(3, id_tuteurOuMaitre);
        ResultSet rs = ps.executeQuery();

        while(rs.next())
        {
            //Pour chaque étudiant récupéré

            int id_etudiant = (Integer) rs.getObject(1);
            String nom = (String) rs.getObject(2);
            String prenom = (String) rs.getObject(3);

            resultat.add(new Etudiant(id_etudiant, nom, prenom));

        }

        c.close();

        return resultat;
    }

    /**
     * Renvoie la liste des étudiants d'un tuteur ou d'un maitre de stage.
     * Pré-requis : tuteurOuMaitre doit être Intervenant ou MaitreDeStage
     * @param tuteurOuMaitre : obtenir la liste du tuteur ou du maitre de stage
     * @param id_tuteurOuMaitre : identifiant du tuteur ou du maitre de stage voulu
     * @return la liste demandée avec seulement le nom, prénom, formation et année des étudiants
     */
    public static List<Etudiant> getEtudiants(Class tuteurOuMaitre, int id_tuteurOuMaitre) throws SQLException
    {
        assert tuteurOuMaitre.equals(Intervenant.class) || tuteurOuMaitre.equals(MaitreDeStage.class);

        List<Etudiant> resultat = new ArrayList<Etudiant>();

        String nomId;
        if(tuteurOuMaitre.equals(Intervenant.class))
            nomId = "id_tuteur";
        else
            nomId = "id_maitrestage";

        String requete = "Select id_etudiant, nom, prenom, f.id_formation, f.intitule, id_annee from etudiant e, formation2 f where " + nomId + " = ? and e.id_formation = f.id_formation order by nom, prenom";
        Connection c = ConnexionBD.getConnection();
        PreparedStatement ps = c.prepareStatement(requete);
        ps.setInt(1, id_tuteurOuMaitre);
        ResultSet rs = ps.executeQuery();

        while(rs.next())
        {
            //Pour chaque étudiant récupéré

            int id_etudiant = (Integer) rs.getObject(1);
            String nom = (String) rs.getObject(2);
            String prenom = (String) rs.getObject(3);
            int id_formation = (Integer) rs.getObject(4);
            String intitule = (String) rs.getObject(5);
            String id_annee = (String) rs.getObject(6);

            Formation form = new Formation(id_formation, intitule);
            Annee annee = new Annee(id_annee);

            resultat.add(new Etudiant(id_etudiant, nom, prenom, form, annee));

        }

        c.close();

        return resultat;
    }

   
}
